﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Threading;
using System.Windows;
using System.Windows.Media.Imaging;
using Heyo.Class.Helper;
using Heyo.Class.Helper.Encryption;
using Heyo.Class.Helper.Network;
using Heyo.Launcher.Models.Data.Game;
using Heyo.Launcher.Util;
using HeyoUi.Annotations;
using HeyoUi.Models;
using HeyoUi.Models.Data.Game;
using HeyoUi.Util;
using Microsoft.Win32;
using Newtonsoft.Json;
using Stylet;

namespace HeyoUi.ViewModel
{
    public class AddNewWindowViewModel : NotifyPropertyChange
    {
        private NonLocalGameInfo _gameVersionSelected;
        private bool _isInstallForge;
        private bool _isInstallOptifine;

        private readonly Dictionary<string, IEnumerable<BmclGetter.ForgeVersion>> ForgeListCache =
            new Dictionary<string, IEnumerable<BmclGetter.ForgeVersion>>();

        private readonly Dictionary<string, IEnumerable<BmclGetter.OptifineVersion>> OptifineListCache =
            new Dictionary<string, IEnumerable<BmclGetter.OptifineVersion>>();

        public AddNewWindowViewModel()
        {
            BmclGetter.GetVersionList().ContinueWith(t =>
            {
                Application.Current.Dispatcher?.Invoke(() =>
                {
                    AllVersions.Clear();
                    ReleaseVersions.Clear();
                    SnapshotVersions.Clear();

                    foreach (NonLocalGameInfo info in t.Result)
                    {
                        AllVersions.Add(info);

                        if (info.Type.ToLower() == "release")
                            ReleaseVersions.Add(info);
                        else
                            SnapshotVersions.Add(info);
                    }
                });
            });
        }

        public string GameName { get; set; }
        public string IconPath { get; set; }
        public bool IsInstalling { get; set; }
        public double TotalInstallProgress { get; set; }
        public string TotalInstallState { get; set; }
        public BindableCollection<InstallProgressVm> InstallProgress { get; set; } 
        public CancellationTokenSource InstallTokenSource=new CancellationTokenSource();

        public void Action_ChoseIconFile()
        {
            FileDialog dialog = new OpenFileDialog();
            dialog.Filter = "图片文件|*.png;*.jpg;*bmp;*.jpeg";
            if (dialog.ShowDialog() != true) return;
            
            string fileName = dialog.FileName;
            IconPath = fileName;
        }

        public void Action_CancelInstall()
        {
            InstallTokenSource.Cancel();
            IsInstalling = false;
        }
        public void Action_StartInstall()
        {
            IsInstalling = true;
            List<FileDownloader> downloads = new List<FileDownloader>();

            string jsonPath = GameLocator.GetJsonPath(GameVersionSelected.Version);
            string jarPath = $"{Environment.CurrentDirectory}\\Temp\\{GameVersionSelected.Version}.jar";
            string forgePath = null;
            string optifinePath = null;
            WebClient client=new WebClient();
            TotalInstallState = "下载游戏json";
            client.DownloadStringTaskAsync(GameVersionSelected.Uri).ContinueWith(downloadTask =>
            {
                string downloadJson = downloadTask.Result;
                TotalInstallState = "解析游戏json";
                McGameJson gameJson = JsonConvert.DeserializeObject<McGameJson>(downloadJson);
                
                if (File.Exists(jarPath))
                {
                    FileStream jarFileStream = File.Open(jarPath, FileMode.Open);
                    TotalInstallState = "游戏jar已存在，正在校验Hash值";
                    string sha1 = Sha1Helper.GetDataSha1(jarFileStream).ToLower();
                    jarFileStream.Close();
                    if (sha1 != gameJson.Downloads.Client.Sha1)
                    {
                        TotalInstallState = "Hash值不匹配，需要重新下载";
                        downloads.Add(new FileDownloader(BmclGetter.GetClientDownloadUrl(GameVersionSelected.Version),
                            jarPath));
                    }

                }
                else
                {
                    downloads.Add(new FileDownloader(BmclGetter.GetClientDownloadUrl(GameVersionSelected.Version),
                        jarPath));
                }


                if (IsInstallForge)
                {
                    forgePath = $"{Environment.CurrentDirectory}\\Temp\\forge-{ForgeVerSelected.McVersion}-{ForgeVerSelected.Version}.jar";
                    if (File.Exists(forgePath))
                    {
                        TotalInstallState = "Forge安装文件已存在，正在校验Hash值";
                        FileStream forgeFileStream = File.Open(forgePath, FileMode.Open);
                        string forgeMd5 = Md5Helper.GetDataMd5(forgeFileStream).ToLower();  //forge用的是md5，醉了
                        forgeFileStream.Close();
                        string trueMd5=ForgeVerSelected.Files.First(t => t.Category == "installer").Hash;
                        if (trueMd5!=null && forgeMd5 != trueMd5)
                        {
                            TotalInstallState = "Hash值不匹配，需要重新下载";
                            downloads.Add(new FileDownloader(BmclGetter.GetForgeDownloadUrl(ForgeVerSelected.Build),
                                forgePath));
                        }
                    }
                    else
                    {
                        downloads.Add(new FileDownloader(BmclGetter.GetForgeDownloadUrl(ForgeVerSelected.Build),
                            forgePath));
                    }
                }

                if (IsInstallOptifine)
                {
                    optifinePath = $"{Environment.CurrentDirectory}\\Temp\\optifine-{OptifineVerSelected.McVersion}-{OptifineVerSelected.Type}-{OptifineVerSelected.Patch}.jar";
                    if (!File.Exists(optifinePath))
                    {
                        downloads.Add(new FileDownloader(
                            BmclGetter.GetOptifineDownloadUrl(OptifineVerSelected.McVersion, OptifineVerSelected.Type,
                                OptifineVerSelected.Patch), optifinePath));
                    }
                }

                TotalInstallState = "正在下载";
                
                InstallProgress =
                    new BindableCollection<InstallProgressVm>(downloads.Select(t => new InstallProgressVm(t)));

                Action completeAction=() =>
                {
                    TotalInstallProgress = 100;
                    string newGameName = GameVersionSelected.Version;
                    TotalInstallState = "写入json";
                    FileHelper.SaveString(jsonPath, downloadJson);
                    TotalInstallState = "移动jar文件";
                    File.Copy(jarPath, GameLocator.GetJarPath(GameVersionSelected.Version), true);
                    if (IsInstallForge)
                    {
                        TotalInstallState = "安装Forge";
                        (_, newGameName) = ForgeInstaller.Install(forgePath,GameName);
                    }

                    if (IsInstallOptifine)
                    {
                        if (IsInstallForge)
                        {
                            OptifineInstaller.InstallWithForge(optifinePath, newGameName);
                        }
                        else
                        {
                            MessageBox.Show("启动器还没有这个功能，先手动装一下吧");
                            Process process = new Process { StartInfo = new ProcessStartInfo(optifinePath) };
                            process.Start();
                        }
                    }

                    HeyoGameJson heyoGameJson=new HeyoGameJson();
                    if(IconPath?.Length>0 && File.Exists(IconPath))
                    {
                        byte[] iconBuffer = new Bitmap(IconPath).ToByteArray();
                        //对byte数组，得到的char+5
                        heyoGameJson.IconData = Base64Helper.GetStringFromData(iconBuffer);
                    }

                    FileStream fs = File.Open(GameLocator.GetGameInfoJsonPath(newGameName), FileMode.Create);
                    StreamWriter writer=new StreamWriter(fs);
                    writer.Write(JsonConvert.SerializeObject(heyoGameJson));
                    writer.Dispose();
                    fs.Close();

                    TotalInstallState = "安装完成";
                };

                if (downloads.Count > 0)
                {
                    FileDownloaderWrap wrap = new FileDownloaderWrap(downloads.ToArray());
                    wrap.OnCompleted += (sender) =>
                    {
                        completeAction?.Invoke();
                    };

                    wrap.OnProgressChanged += (sender) =>
                    {
                        TotalInstallProgress = sender.Progress;
                    };
                    wrap.BeginDownload(InstallTokenSource);
                }
                else
                {
                    completeAction?.Invoke();
                }
                
            });


        }

        public bool IsInstallForge
        {
            get => _isInstallForge;
            set
            {
                _isInstallForge = value;
                if (value)
                    UpdateForgeList();
            }
        }

        public bool IsInstallOptifine
        {
            get => _isInstallOptifine;
            set
            {
                _isInstallOptifine = value;
                if (value)
                    UpdateOptifineList();
            }
        }

        public bool IsInstallLiteLoader { get; set; }

        public NonLocalGameInfo GameVersionSelected
        {
            get => _gameVersionSelected;
            set
            {
                _gameVersionSelected = value;
                if (IsInstallForge) UpdateForgeList();

                if (IsInstallOptifine) UpdateOptifineList();
            }
        }

        public BmclGetter.OptifineVersion OptifineVerSelected { get; set; }

        public BmclGetter.ForgeVersion ForgeVerSelected { get; set; }

        public BindableCollection<BmclGetter.ForgeVersion> ForgeCollection { get; } =
            new BindableCollection<BmclGetter.ForgeVersion>();

        public BindableCollection<string> LiteLoaderCollection { get; } = new BindableCollection<string>();

        public BindableCollection<BmclGetter.OptifineVersion> OptifineCollection { get; } =
            new BindableCollection<BmclGetter.OptifineVersion>();

        public BindableCollection<NonLocalGameInfo> AllVersions { get; } =
            new BindableCollection<NonLocalGameInfo>();

        public BindableCollection<NonLocalGameInfo> ReleaseVersions { get; } =
            new BindableCollection<NonLocalGameInfo>();

        public BindableCollection<NonLocalGameInfo> SnapshotVersions { get; } =
            new BindableCollection<NonLocalGameInfo>();

        private void UpdateOptifineList()
        {
            if (GameVersionSelected == null) return;
            OptifineCollection.Clear();
            if (!OptifineListCache.ContainsKey(GameVersionSelected.Version))
                BmclGetter.GetOptifineList(GameVersionSelected.Version).ContinueWith(t =>
                {
                    Application.Current.Dispatcher.Invoke(() =>
                    {
                        List<BmclGetter.OptifineVersion> sorted = t.Result.ToList();
                        sorted.Sort((a, b) => -string.Compare(a.Patch, b.Patch, StringComparison.Ordinal));
                        OptifineListCache.Add(GameVersionSelected.Version, sorted);
                        OptifineCollection.AddRange(sorted);
                    });
                });
            else
                OptifineCollection.AddRange(OptifineListCache[GameVersionSelected.Version]);
        }

        private void UpdateForgeList()
        {
            if (GameVersionSelected == null) return;
            ForgeCollection.Clear();
            if (!ForgeListCache.ContainsKey(GameVersionSelected.Version))
                BmclGetter.GetForgeList(GameVersionSelected.Version).ContinueWith(t =>
                {
                    Application.Current.Dispatcher.Invoke(() =>
                    {
                        List<BmclGetter.ForgeVersion> sorted = t.Result.ToList();
                        sorted.Sort((a,b)=> -string.CompareOrdinal(a.Version,b.Version));
                        ForgeListCache.Add(GameVersionSelected.Version, sorted);
                        ForgeCollection.AddRange(sorted);
                    });
                });
            else
                ForgeCollection.AddRange(ForgeListCache[GameVersionSelected.Version]);
        }
    }

    public class InstallProgressVm : NotifyPropertyChange
    {
        public InstallProgressVm(FileDownloader downloader)
        {
            downloader.OnProgressChanged += _downloader_OnProgressChanged;
            FileName = Path.GetFileName(downloader.LocalPath);
        }

        private void _downloader_OnProgressChanged(FileDownloader obj)
        {
            Speed = obj.Speed;
            Progress = obj.Progress;
        }

        public string FileName { get; set; }
        public double Speed { get; set; }
        public double Progress { get; set; }
    }
}